package com.sxt.generate.table;

import com.sxt.generate.util.SqlUtil;
import com.sxt.generate.util.StringTool;
import org.apache.commons.lang.StringUtils;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @ClassName: Configuration
 * @Description:Configuration
 * @version:v1.0.0
 * @author: haning
 * @date:
 */
public class TableFactory {

    private String driver;
    private String url;
    private String username;
    private String password;
    private String schema;
    private String catalog;
    private Connection connection;

    public String getCatalog() {
        return catalog;
    }

    public void setCatalog(String catalog) {
        this.catalog = catalog;
    }

    public String getSchema() {
        return schema;
    }

    public void setSchema(String schema) {
        this.schema = schema;
    }



    public void loadJdbcDriver(String driver) {
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException ex) {
            System.out.println("[generate-error] not found jdbc driver class:" + driver + ":" + ex);
        }
    }

    public Connection getConnection() {
        try {
            if (this.connection == null || this.connection.isClosed()) {
                loadJdbcDriver(driver);
                this.connection = DriverManager.getConnection(url, username, password);
            }
        } catch (Exception ex) {
            System.out.println("[generate-error] connect failed ,please check the configuration" + url + ":" + username + ":" + password + ":" + ex);
        }
        return this.connection;
    }

    public DatabaseMetaData getMetaData() {

        DatabaseMetaData metaData = null;
        if (this.connection != null) {
            try {
                metaData = this.connection.getMetaData();
            } catch (java.sql.SQLException ex) {
                ex.printStackTrace();
            }
        }
        return metaData;
    }


    public Table getTable(String tableName, String driver, String url, String username, String password) {
        this.driver = driver;
        this.url = url;
        this.username = username;
        this.password = password;
        if("oracle".equals(this.getDatabaseType(driver))){
            this.schema = username;
        }
        return getTableInfo(tableName);

    }

    public Table getTableInfo(String tableName) {

        this.catalog = StringUtils.defaultIfEmpty(getCatalog(), null);
        this.schema = StringUtils.defaultIfEmpty(getSchema(), null);
        if (this.catalog != null) this.catalog = StringUtils.upperCase(this.catalog);
        if (this.schema != null) this.schema = StringUtils.upperCase(this.schema);

        Connection conn = this.getConnection();
        DatabaseMetaData dbMetaData = this.getMetaData();

        assert StringUtils.isNotBlank(tableName) : "tableName can't null";
        assert conn != null : "connection can't null";

        try {
            ResultSet rs = dbMetaData.getTables(this.catalog, this.schema, /*StringUtils.upperCase(tableName)*/tableName, null);

            if (rs.next()) {

                String table_Name = rs.getString("TABLE_NAME");
                String table_Type = rs.getString("TABLE_TYPE");
                String schema_Name = StringUtils.defaultIfEmpty(rs.getString("TABLE_SCHEM"), "");
                String remarks = rs.getString("REMARKS");
                if (remarks == null && SqlUtil.isOracleDataBase(this.connection)) {
                    remarks = StringUtils.defaultIfEmpty(SqlUtil.getOracleTableComments(connection, table_Name), "");
                }

                Table table = new Table();
                table.setTableName(tableName);
                table.setRemark(remarks);
                table.setTableNameAlias(tableName);
                initTableColumns(table);
                table.initialize();
                return table;

            } else {
                System.out.println("can not find table:" + tableName);

            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public void initTableColumns(Table table) {
        this.catalog = StringUtils.defaultIfEmpty(getCatalog(), null);
        this.schema = StringUtils.defaultIfEmpty(getSchema(), null);
        List<String> primaryKeys = this.getTablePrimaryKeys(table);
        List<String> indexInfos = this.getTableIndexInfo(table);
        List<String> indexUniques = this.getTableIndexUnique(table);

        try {
            ResultSet columnRs = this.getMetaData().getColumns(catalog, schema, table.getTableName(), null);
            System.out.println("[<< tableinfo >>]" + catalog + "|" + schema + "|" + table.getTableName());
            while (columnRs.next()) {
                int data_Type = columnRs.getInt("DATA_TYPE");
                String type_Name = columnRs.getString("TYPE_NAME");
                String column_Name = columnRs.getString("COLUMN_NAME");
                int column_Size = columnRs.getInt("COLUMN_SIZE");
                int decimal_Digits = columnRs.getInt("DECIMAL_DIGITS");
                String column_Default = columnRs.getString("COLUMN_DEF");
                String remarks = columnRs.getString("REMARKS");

                if (remarks == null && SqlUtil.isOracleDataBase(connection)) {
                    remarks = StringUtils.defaultIfEmpty(SqlUtil.getOracleColumnComments(connection, table.getTableName(), column_Name), "");
                }

                // if columnNoNulls or columnNullableUnknown assume "not nullable"
                boolean isNullable = (DatabaseMetaData.columnNullable == columnRs.getInt("NULLABLE"));
                //boolean is_AutoIncrement =  ("YES".equals(columnRs.getString("IS_AUTOINCREMENT")))

                boolean isPk = primaryKeys.contains(column_Name);
                boolean isIndexed = indexInfos.contains(column_Name);
                boolean isUnique = indexUniques.contains(column_Name);

                Column column = new Column();

                column.setSqlType(data_Type);
                column.setColumnName(column_Name);
//                column.setFieldName(StringTool.toCamel(column_Name));
                column.setFieldName(column_Name);
                column.setColumnSize(column_Size);
                column.setDecimalDigits(decimal_Digits);
                column.setSqlTypeName(type_Name);
                column.setRemark(remarks);
                column.setNullable(isNullable);
                column.setPrimaryKey(isPk);
                column.setUnique(isUnique);
                column.setDefaultValue(column_Default);
                //column.setAutoIncrement(is_AutoIncrement)
                column.initialize();
                if (column.isPrimaryKey()) {
                    table.getPkColumns().add(column);
                } else {
                    table.getNotPkColumns().add(column);
                }
                table.getColumns().add(column);
            }

            columnRs.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }

    public List<String> getTablePrimaryKeys(Table table) {
        this.catalog = StringUtils.defaultIfEmpty(getCatalog(), null);
        this.schema = StringUtils.defaultIfEmpty(getSchema(), null);
        List<String> primaryKeys = new ArrayList<String>();
        try {
            ResultSet pkRs = this.getMetaData().getPrimaryKeys(catalog, schema, table.getTableName());
            while (pkRs.next()) {
                //var pkName = pkRs.getString("PK_NAME")
                primaryKeys.add(pkRs.getString("COLUMN_NAME"));
            }
            pkRs.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return primaryKeys;
    }

    public List<String> getTableIndexInfo(Table table) {
        this.catalog = StringUtils.defaultIfEmpty(getCatalog(), null);
        this.schema = StringUtils.defaultIfEmpty(getSchema(), null);
        List<String> indexColums = new ArrayList<String>();
        try {
            ResultSet indexRs = this.getMetaData().getIndexInfo(catalog, schema, table.getTableName(), false, true);
            while (indexRs.next()) {
                String columnName = indexRs.getString("COLUMN_NAME");
                String indexName = indexRs.getString("INDEX_NAME");
                if (columnName != null) indexColums.add(columnName);
            }
            indexRs.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return indexColums;
    }

    public List<String> getTableIndexUnique(Table table) {
        catalog = StringUtils.defaultIfEmpty(getCatalog(), null);
        schema = StringUtils.defaultIfEmpty(getSchema(), null);
        List<String> uniqueIndexColums = new ArrayList<String>();
        try {
            ResultSet indexRs = this.getMetaData().getIndexInfo(this.catalog, this.schema, table.getTableName(), true, true);
            while (indexRs.next()) {
                String columnName = indexRs.getString("COLUMN_NAME");
                String indexName = indexRs.getString("INDEX_NAME");
                if (columnName != null) uniqueIndexColums.add(columnName);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return uniqueIndexColums;
    }

    public String getDatabaseType(String driver){
        if(driver.contains("mysql")){
            return "mysql";
        }
        else if(driver.contains("oracle")){
            return "oracle";
        }
        return "";
    }

}
